import cv2 as cv
import numpy as np


# 高斯金字塔
def pyramid_demo(image):
    """
    高斯金字塔
    :param image: 输入图像
    :return: 图像金字塔
    """
    level = 3  # 设置金字塔的层数为3
    pyramid_images = []  # 建立一个空列表
    for i in range(level):
        dst = cv.pyrDown(image)   # 先对图像进行高斯平滑，然后再进行降采样（将图像尺寸行和列方向缩减一半）
        # 参数dstsize表示降采样之后的目标图像的大小。它是有默认值的
        pyramid_images.append(dst)  # 在列表末尾添加新的对象
        # cv.imshow("pyramid"+str(i), dst)
        image = dst.copy()
    return pyramid_images


# 拉普拉斯金字塔
def lapalian_demo(image):
    pyramid_images = pyramid_demo(image)    # 做拉普拉斯金字塔必须用到高斯金字塔的结果
    level = len(pyramid_images)
    for i in range(level-1, -1, -1):
        if i == 0:
            expand = cv.pyrUp(pyramid_images[i], dstsize=image.shape[:2])
            lpls = cv.subtract(image, expand)
            cv.imshow("lapalian_down_"+str(i), lpls)
        else:
            h, w = pyramid_images[i - 1].shape[:2]
            expand = cv.pyrUp(pyramid_images[i], dstsize=(h, w))  # 保证第i层上采样后的图像大小与i-1层的图像大小相等
            lpls = cv.subtract(pyramid_images[i-1], expand)
            cv.imshow("lapalian_down_"+str(i), lpls)


# 进行图像缩放可以用图像金字塔，也可以使用resize函数进行缩放，后者效果更好。
# 两种类型的金字塔：
# ①高斯金字塔：用于下采样。高斯金字塔是最基本的图像塔。原理：首先将原图像作为最底层图像G0（高斯金字塔的第0层），利用高斯核（5*5）对其进行卷积，然后对卷积后的图像进行下采样（去除偶数行和列）得到上一层图像G1，将此图像作为输入，重复卷积和下采样操作得到更上一层图像，反复迭代多次，形成一个金字塔形的图像数据结构，即高斯金字塔。
# ②拉普拉斯金字塔：用于重建图像，也就是预测残差，对图像进行最大程度的还原。比如一幅小图像重建为一幅大图，原理：用高斯金字塔的每一层图像减去其上一层图像上采样并高斯卷积之后的预测图像，得到一系列的差值图像即为 LP 分解图像。
# 两种类型的采样：
# ①上采样：就是图片放大（所谓上嘛，就是变大），使用PryUp函数。上采样步骤：先将图像在每个方向放大为原来的两倍，新增的行和列用0填充，再使用先前同样的内核与放大后的图像卷积，获得新增像素的近似值。
# ②下采样：就是图片缩小（所谓下嘛，就是变小），使用PryDown函数。下采样步骤：先对图像进行高斯内核卷积 ，再将所有偶数行和列去除。
# 总之，上下采样都存在一个严重的问题，那就是图像变模糊了，因为缩放的过程中发生了信息丢失的问题。要解决这个问题，就得用拉普拉斯金字塔。
src = cv.imread("imgs/test008.jpg")
cv.imshow("wangbobo", src)
src = cv.resize(src, (512, 512))  # 因为在降级的时候，奇数会加一除以2，但是在升级的时候不会还原，因此图片的大小最好是2^n倍，因此在最开始时使用resize函数来缩放图像
lapalian_demo(src)
cv.waitKey(0)
cv.destroyAllWindows()
